﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NClass.Core.HtmlRepresentation
{
	public class HtmlDocumentBuilder
	{
		#region tags
		public const string TITLE_TAG = "<!--title-->";
		public const string PROJECT_TABLE_TAG = "<!--project_table-->";
		public const string CLASS_TABLES_TAG = "<!--class_tables-->";
		public const string INTERFACE_TABLES_TAG = "<!--interface_tables-->";
		public const string CLASS_DEFINITION_TAG = "<!--class_definition-->";
		public const string INTERFACE_DEFINITION_TAG = "<!--interface_definition-->";
		public const string FIELDS_DEFINITION_TAG = "<!--fields_definition-->";
		public const string METHODS_DEFINITION_TAG = "<!--methods_definition-->";
		public const string RELATIONS_DEFINITION_TAG = "<!--relations_definition-->";
		public const string SYSTEM_BRACKET_SIZE_TAG = "<!--system_bracket_size-->";
		#endregion

		#region templates
		public const string DOCUMENT_TEMPLATE =
			"<html>\n" +
			"<head>\n" +
			"<title>\n" +
			TITLE_TAG +
			"\n</title>\n" +
			"<style type=\"text/css\">\n" +
			"body{\n" +
			"font-family:Courier New;\n" +
			"}\n" +
			".system_bracket_size{\n" +
			"font-size: <!--system_bracket_size-->pt;\n" +
			"}\n" +
			"</style>\n" +
			"</head>\n" +
			"<body>\n" +
			PROJECT_TABLE_TAG +
			"\n</body>\n" +
			"</html>\n";

		public const string PROJECT_TABLE_TEMPLATE =
			"<table border=\"0\">\n" +
			"<tr>\n" +
			"<td valign=\"middle\">\n" +
			"<font class=\"system_bracket_size\">\n</font>" +
			"</td>\n" +
			"<td>\n" +
			CLASS_TABLES_TAG +
			"\n</td>\n" +
			"</tr>\n" +
			"<tr>\n" +
			"<td valign=\"middle\">\n" +
			"<font class=\"system_bracket_size\">\n</font>" +
			"</td>\n" +
			"<td>\n" +
			INTERFACE_TABLES_TAG +
			"\n</td>\n" +
			"</tr>\n" +
			"</table>\n";

		public const string SINGLE_CLASS_TABLE_TEMPLATE =
			"<table border=\"0\">\n" +
			"<tr>\n" +
			"<td valign=\"middle\">\n" +
			"<font style=\"font-size: 85pt\">{\n</font>" +
			"</td>\n" +
			"<td valign=\"middle\">\n" +
			CLASS_DEFINITION_TAG +
			"\n<br />\n" +
			FIELDS_DEFINITION_TAG +
			"\n<br />\n" +
			METHODS_DEFINITION_TAG +
			"\n<br />\n" +
			RELATIONS_DEFINITION_TAG +
			"\n<br />\n" +
			"\n</td>\n" +
			"</tr>\n" +
			"</table>\n" +
			"<br />\n\n";

		public const string SINGLE_INTERFACE_TABLE_TEMPLATE =
			"<table border=\"0\">\n" +
			"<tr>\n" +
			"<td valign=\"middle\">\n" +
			"<font style=\"font-size: 85pt\">{\n</font>" +
			"</td>\n" +
			"<td valign=\"middle\">\n" +
			INTERFACE_DEFINITION_TAG +
			"\n<br />\n" +
			METHODS_DEFINITION_TAG +
			"\n<br />\n" +
			"\n</td>\n" +
			"</tr>\n" +
			"</table>\n" +
			"<br />\n\n";
		#endregion

		public const int SYSTEM_BRACKET_SIZE_PER_CLASS = 125;

		private Dictionary<ClassType, string> m_classHtmlCodes = new Dictionary<ClassType, string>();
		private List<ClassType> m_classes = new List<ClassType>();

		private Dictionary<InterfaceType, string> m_interfaceHTMLCodes = new Dictionary<InterfaceType, string>();

		private string m_currentTitle = TITLE_TAG;

		public HtmlDocumentBuilder()
		{
		}

		/// <summary>
		/// Property of Html document title
		/// </summary>
		public string Title
		{
			get
			{
				return m_currentTitle;
			}
			set
			{
				m_currentTitle = value;
			}
		}

		protected void AddClass(ClassType entitie)
		{
			m_classes.Add(entitie);
			m_classHtmlCodes.Add(entitie, GenerateSingleClassTable(entitie));
		}

		protected void AddInterface(InterfaceType i)
		{
			m_interfaceHTMLCodes.Add(i, GenerateSingleInterfaceTable(i));
		}

		private string GenerateSingleClassTable(ClassType entitie)
		{
			string result = string.Empty;
			result = SINGLE_CLASS_TABLE_TEMPLATE;
			result = result.Replace(CLASS_DEFINITION_TAG, GenerateClassDefinition(entitie));
			result = result.Replace(FIELDS_DEFINITION_TAG, GenerateFieldsDefinition(entitie.Fields));
			result = result.Replace(METHODS_DEFINITION_TAG, GenerateOperationsDefinition(entitie.Operations));
			result = result.Replace(RELATIONS_DEFINITION_TAG, GenerateRelationsDefinition(entitie));
			return result;
		}

		private string GenerateSingleInterfaceTable(InterfaceType i)
		{
			string result = string.Empty;
			result = SINGLE_INTERFACE_TABLE_TEMPLATE;
			result = result.Replace(INTERFACE_DEFINITION_TAG, GenerateInterfaceDefinition(i));
			result = result.Replace(METHODS_DEFINITION_TAG, GenerateOperationsDefinition(i.Operations));
			return result;
		}

		private string GenerateClassDefinition(ClassType entitie)
		{
			string result = string.Empty;
			string isAbstract = entitie.Modifier == ClassModifier.Abstract ? "<sup>a</sup>" : string.Empty;
			result += "<a name=\"class_" + entitie.Name + "\"></a>";
			result += "C" + isAbstract + "(" + entitie.Name + ") = A x B";
			return result;
		}

		private string GenerateInterfaceDefinition(InterfaceType i)
		{
			string result = string.Empty;
			result += "<a name=\"interface_" + i.Name + "\"></a>";
			result += "I(" + i.Name + ") = B";
			return result;
		}

		private string GenerateFieldsDefinition(IEnumerable<Field> fields)
		{
			string result = string.Empty;
			bool isfirst = true;
			foreach (Field f in fields)
			{
				string localResult = string.Empty;
				localResult = isfirst ? "C(" + f.Name + ")" : ", C(" + f.Name + ")";
				result += localResult;
				isfirst = isfirst ? false : false;
			}
			result = "A = {" + result + "}";
			return result;
		}

		private string GenerateOperationsDefinition(IEnumerable<Operation> operations)
		{
			string result = string.Empty;
			bool isfirst = true;
			foreach (Operation o in operations)
			{
				result += isfirst ? o.Name : ", " + o.Name;
				isfirst = isfirst ? false : false;
			}
			result = "B = {" + result + "}";
			return result;
		}

		private string GenerateRelationsDefinition(ClassType entitie)
		{
			string result = string.Empty;

			string associations = string.Empty;


			string inheritance = string.Empty;
			if (entitie.ImplementsInterface || entitie.BaseClass != null)
			{
				inheritance += "F(C" +
					(entitie.Modifier == ClassModifier.Abstract ? "<sup>a</sup>" : string.Empty) +
					"(" + entitie.Name + "))<sup>inh</sup> = ";
				bool startToUseUnions = false;
				if (entitie.BaseClass != null)
				{
					inheritance += "F(C" +
						(entitie.BaseClass.Modifier == ClassModifier.Abstract ? "<sup>a</sup>" : string.Empty) +
						"(<a href=\"#class_" + entitie.BaseClass.Name +"\">" + entitie.BaseClass.Name + "</a>))";
					startToUseUnions = true;
				}
				/////////////////////////interfaces
				if (entitie.ImplementsInterface)
				{
					foreach (InterfaceType interf in entitie.Interfaces)
					{
						if (startToUseUnions)
						{
							inheritance += " <font face=\"Arial\">U</font> I(<a href=\"#interface_" + interf.Name + "\">" + interf.Name + "</a>)";
						}
						else
						{
							inheritance += " I(<a href=\"#interface_" + interf.Name + "\">" + interf.Name + "</a>)";
							startToUseUnions = true;
						}
					}
				}
				inheritance += "\n";
			}
			result += associations + inheritance;

			return result;
		}

		public string Build(Project project)
		{
			foreach (Model item in project.Items)
			{
				foreach (IEntity entity in item.Entities)
				{
					switch (entity.EntityType)
					{
						case EntityType.Class:
							{
								AddClass((ClassType)entity);
								break;
							}
						case EntityType.Interface:
							{
								AddInterface((InterfaceType)entity);
								break;
							}
						default:
							{
								break;
							}
					}
				}
			}
			Title = Title == TITLE_TAG ? project.Name : Title;
			return BuildHtml();
		}

		private string BuildHtml()
		{
			string result = DOCUMENT_TEMPLATE;
			result = result.Replace(TITLE_TAG, Title);
			string classesHtmlCode = string.Empty;
			string interfacesHtmlCode = string.Empty;
			foreach (ClassType c in m_classes)
			{
				classesHtmlCode += m_classHtmlCodes[c];
			}
			foreach (InterfaceType i in m_interfaceHTMLCodes.Keys)
			{
				interfacesHtmlCode += m_interfaceHTMLCodes[i];
			}
			string projectTable = PROJECT_TABLE_TEMPLATE;
			projectTable = projectTable.Replace(CLASS_TABLES_TAG, classesHtmlCode);
			projectTable = projectTable.Replace(INTERFACE_TABLES_TAG, interfacesHtmlCode);

			result = result.Replace(PROJECT_TABLE_TAG, projectTable);
			result = result.Replace(SYSTEM_BRACKET_SIZE_TAG, (SYSTEM_BRACKET_SIZE_PER_CLASS * m_classes.Count).ToString());
			return result;
		}
	}
}
